﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using JiveMessenger.Models;
using JiveMessenger.User;
using JmShared;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using System;
using System.Diagnostics;
using JiveMessenger.Utilities;
using System.Collections.Specialized;
using Windows.UI.Xaml.Data;
using Windows.UI.Text;
using Windows.UI.Xaml.Media;
using Windows.UI;
using System.Threading.Tasks;
using System.IO;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using Windows.UI.Core;
using GalaSoft.MvvmLight.Threading;
using GalaSoft.MvvmLight.Views;
using JiveMessenger.Log;


namespace JiveMessenger.ViewModels
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class ViewModelContactList : ViewModelBaseEx, IViewModelContactList
    {
        public enum MessengerCommands
        {
            UpdateCustomEmoticons,
            /// <summary>
            /// string username
            /// </summary>
            SetContactAsOffline,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetContactFromUsername,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetContactFromUserID,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetCurrentUserContact,
            /// <summary>
            /// returns collection of IContact
            /// </summary>
            GetContacts,
            /// <summary>
            /// ClientStatus manual client status
            /// </summary>
            UpdateManualClientStatus,
            /// <summary>
            /// Clear the contact list
            /// </summary>
            ClearContactList,
            /// <summary>
            /// Create the visual groups that populates the contact list
            /// </summary>
            CreateVisualGroups

        }


        private bool _isConnected;
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private INavigationService _navigationService;
        private IClient _currentClient;
        private readonly object LockObject = new object();
        public RelayCommand OpenAddContactPageCommand { get; set; }
        public RelayCommand OpenGroupAndContactPageCommand { get; set; }
        public RelayCommand CreateGroupCommand { get; set; }
        public RelayCommand<IChatGroup> InviteContactToGroupCommand { get; set; }
        public RelayCommand<IChatGroup> LeaveGroupCommand { get; set; }
        public RelayCommand<IChatGroup> BlockGroupCommand { get; set; }
        public RelayCommand<IContact> OpenPmCommand { get; set; }
        public RelayCommand OpenNotificationsCommand { get; set; }
        public RelayCommand<IContact> IgnoreContactCommand { get; set; }
        public RelayCommand<IChatGroup> OpenGroupCommand { get; set; }
        public RelayCommand<IChatGroup> ManageGroupCommand { get; set; }
        public RelayCommand<IContact> AddCustomEmoticonsCommand { get; set; }
        public RelayCommand AddEmoticonCommand { get; set; }
        public RelayCommand<IEmoticon> DeleteEmoticonCommand { get; set; }


        private ObservableCollection<IContact> _contact;
        public ObservableCollection<IContact> Contacts
        {
            get { return _contact; }
            set
            {
                if (_contact != value)
                {
                    Set(() => Contacts, ref _contact, value);
                }
            }
        }


        private ObservableCollection<VisualGroup> _groupedContacts;
        public ObservableCollection<VisualGroup> GroupedContacts
        {
            get { return _groupedContacts; }
            set
            {
                if (_groupedContacts != value)
                {
                    Set(() => GroupedContacts, ref _groupedContacts, value);
                }
            }
        }


        private IContact _currentUser;
        public IContact CurrentUser
        {
            get { return _currentUser; }
            set
            {
                if (_currentUser != value)
                {
                    if (value != null && value is JiveContactModel)
                    {
                        ((JiveContactModel)value).PropertyChanged -= CurrentUser_PropertyChanged;
                    }
                    Set(() => CurrentUser, ref _currentUser, value);
                    if (value != null && value is JiveContactModel)
                    {
                        ((JiveContactModel)value).PropertyChanged += CurrentUser_PropertyChanged;
                    }
                }
            }
        }

        private ClientStatus _manualClientStatus;
        public ClientStatus ManualClientStatus
        {
            get { return _manualClientStatus; }
            set
            {
                if (_manualClientStatus != value)
                {
                    Set(() => ManualClientStatus, ref _manualClientStatus, value);
                }
            }
        }



        /// <summary>
        /// Initializes a new instance of the ViewModelContactList class.
        /// </summary>
        public ViewModelContactList(IClient currentClient, INavigationService navService)
        {
            _navigationService = navService;
            _currentClient = currentClient;
            GroupedContacts = new ObservableCollection<VisualGroup>();

            OpenAddContactPageCommand = new RelayCommand(OpenAddContactPageCommandAction, () => _currentClient.IsLoggedIn);
            OpenGroupAndContactPageCommand = new RelayCommand(OpenGroupAndContactPageCommandAction, () => _currentClient.IsLoggedIn);
            CreateGroupCommand = new RelayCommand(CreateGroupCommandAction);
            InviteContactToGroupCommand = new RelayCommand<IChatGroup>(InviteContactToGroupCommandAction, CanExecuteAsGroupAdmin);
            LeaveGroupCommand = new RelayCommand<IChatGroup>(LeaveGroupCommandAction, CanExecuteLeaveGroup);
            BlockGroupCommand = new RelayCommand<IChatGroup>(BlockGroupCommandAction, CanExecuteBlockGroup);
            OpenPmCommand = new RelayCommand<IContact>(OpenPmCommandAction);
            OpenNotificationsCommand = new RelayCommand(OpenNotificationsCommandAction);
            IgnoreContactCommand = new RelayCommand<IContact>(IgnoreContactCommandAction);
            OpenGroupCommand = new RelayCommand<IChatGroup>(OpenGroupCommandAction);
            ManageGroupCommand = new RelayCommand<IChatGroup>(ManageGroupCommandAction, CanExecuteAsGroupAdmin);
            AddCustomEmoticonsCommand = new RelayCommand<IContact>(AddCustomEmoticonsCommandAction);
            DeleteEmoticonCommand = new RelayCommand<IEmoticon>(DeleteEmoticonCommandAction);
            AddEmoticonCommand = new RelayCommand(AddEmoticonCommandAction);
            this.PropertyChanged += ViewModelContactList_PropertyChanged;

            Messenger.Default.Register<NotificationMessage>(this, async msg =>
                {
                    switch (msg.Notification)
                    {
                        case "UpdateCustomEmoticons":
                            if (this.CurrentUser is JiveContactModel)
                            {
                                if (_currentClient.UserSettings.CustomEmoticons == null)
                                    return;
                                ((JiveContactModel)this.CurrentUser).CustomEmoticons = CreateCustomEmoticonList(this.CurrentUser, _currentClient.UserSettings.CustomEmoticons);
                                await Task.Delay(200);
                            }
                            break;
                    }
                });
            Messenger.Default.Register<NotificationMessage<string>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "SetContactAsOffline":
                        {
                            SetContactAsOffline(msg.Content);
                        }
                        break;
                    case "CreateVisualGroups":
                        this.GroupedContacts = CreateVisualGroups();
                        break;
                }
            });
            Messenger.Default.Register<NotificationMessage<ClientStatus>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "UpdateManualClientStatus":
                            this.ManualClientStatus = msg.Content;
                            break;
                    }
                });
            Messenger.Default.Register<NotificationMessageAction<IEnumerable<IContact>>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "GetContacts":
                        msg.Execute(Contacts);
                        break;
                }
            });
            Messenger.Default.Register<NotificationMessageAction<IContact>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "GetContactFromUsername":
                        {
                            string targetUsername = msg.Target.ToString();
                            IContact contact = null;

                            if (targetUsername == JiveContactModel.GreenText.UserName)
                            {
                                contact = JiveContactModel.GreenText;
                            }
                            else if (targetUsername == JiveContactModel.RedText.UserName)
                            {
                                contact = JiveContactModel.RedText;
                            }
                            else if (targetUsername == JiveContactModel.GrayText.UserName)
                            {
                                contact = JiveContactModel.GrayText;
                            }
                            else if (this.Contacts != null)
                            {
                                contact = this.Contacts.FirstOrDefault(x => x.UserName == targetUsername);
                            }
                            msg.Execute(contact);
                            break;
                        }
                    case "GetContactFromUserID":
                        {

                            string targetUserID = msg.Target.ToString();
                            IContact contact = null;
                            if (targetUserID == JiveContactModel.GreenText.UserID)
                            {
                                contact = JiveContactModel.GreenText;
                            }
                            else if (targetUserID == JiveContactModel.RedText.UserID)
                            {
                                contact = JiveContactModel.RedText;
                            }
                            else if (targetUserID == JiveContactModel.GrayText.UserID)
                            {
                                contact = JiveContactModel.GrayText;
                            }
                            else if (this.Contacts != null)
                            {
                                contact = this.Contacts.FirstOrDefault(x => x.UserID == targetUserID);
                            }
                            msg.Execute(contact);


                            break;
                        }
                    case "GetCurrentUserContact":
                        {
                            msg.Execute(this.CurrentUser);
                            break;
                        }
                    case "ClearContactList":
                        this.Contacts = new ObservableCollection<IContact>();
                        this.GroupedContacts = new ObservableCollection<VisualGroup>();
                        break;
                }
            });
        }

        private void OpenGroupAndContactPageCommandAction()
        {
            _navigationService.NavigateTo("GroupAndContactNavigationPage");
        }

        private async void OpenAddContactPageCommandAction()
        {
            // _navigationService.NavigateTo("AddContactPage");
            await Task.Delay(50);
            _navigationService.NavigateTo("AddContactPage");
        }

        private void AddEmoticonCommandAction()
        {
            _navigationService.NavigateTo("AddEmoticonPage");
        }

        private void DeleteEmoticonCommandAction(IEmoticon obj)
        {
            ContactEmoticon emoticon = _currentClient.CurrentUserInfo.CustomEmoticons.FirstOrDefault(x => x.Keyword == ((JiveCustomEmoticonModel)obj).EmoticonShortcut);
            _currentClient.CurrentUserInfo.CustomEmoticons.Remove(emoticon);
            this.CurrentUser.CustomEmoticons = CreateCustomEmoticonList(CurrentUser, _currentClient.CurrentUserInfo.CustomEmoticons);
        }




        private async void AddCustomEmoticonsCommandAction(IContact contact)
        {
            await Task.Delay(100);
            _navigationService.NavigateTo("GetUserEmoticonsPage", new Tuple<bool, IContact>(false, contact));
        }

        async void ViewModelContactList_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ManualClientStatus":
                    if (_isConnected)
                        await _currentClient.ChangeManualClientStatus(this.ManualClientStatus);
                    break;
            }
        }


        private void CurrentUser_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CustomEmoticons")
            {
                ((JiveContactModel)sender).CustomEmoticons.CollectionChanged -= CustomEmoticons_CollectionChanged;
                ((JiveContactModel)sender).CustomEmoticons.CollectionChanged += CustomEmoticons_CollectionChanged;
            }
        }
        void CustomEmoticons_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var v in e.NewItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce == null)
                        {
                            this._currentClient.UserSettings.CustomEmoticons.Add(new ContactEmoticon()
                            {
                                Extension = Path.GetExtension(m.EmoticonPath),
                                Keyword = m.EmoticonShortcut
                            });
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var v in e.OldItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce != null)
                            this._currentClient.UserSettings.CustomEmoticons.Remove(ce);
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    foreach (var v in e.OldItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce != null)
                            this._currentClient.UserSettings.CustomEmoticons.Remove(ce);
                        this._currentClient.UserSettings.CustomEmoticons.Add(new ContactEmoticon() { Keyword = m.EmoticonShortcut, Extension = Path.GetExtension(m.EmoticonPath) });
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    this._currentClient.UserSettings.CustomEmoticons.Clear();
                    break;
                default:
                    break;
            }
        }
        //private bool CanExecuteAddContact(IContact contact)
        //{
        //    if (contact != null && _currentClient.FriendUsernames.Contains(contact.UserName, StringComparer.OrdinalIgnoreCase))
        //        return false;



        //    return true;
        //}

        private async void ManageGroupCommandAction(IChatGroup group)
        {
            //   Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, group, "OpenManageGroup"));
            await Task.Delay(100);
            _navigationService.NavigateTo("ManageGroupPage", new Tuple<bool, IChatGroup>(false, group));
        }

        private void OpenGroupCommandAction(IChatGroup group)
        {
            ViewModelChat vm = null;
            if (!_currentClient.ClosedConversations.TryRemove(group.ID, out vm))
            {
                vm = new ViewModelGroupChat(_currentClient, group) { IsPinned = _currentClient.UserSettings.OpenChats.Any(x => x.ID == group.ID && x.IsPinned == true) };
            }
            Messenger.Default.Send(new NotificationMessage<ViewModelChat>(this, vm, "OpenGroup"));
        }



        private async void BlockGroupCommandAction(IChatGroup iGroup)
        {

            switch (iGroup.Type)
            {
                case GroupType.JiveChatGroup:
                    {
                        JiveChatGroupModel group = (JiveChatGroupModel)iGroup;
                        if (group.IsBlocked)
                        {
                            await _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.UnblockGroup) { RecipientID = group.ID });
                            _currentClient.UserSettings.BlockedGroups.Remove(group.ID);
                            group.IsBlocked = false;
                        }
                        else
                        {
                            await _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.BlockGroup) { RecipientID = group.ID });
                            _currentClient.UserSettings.BlockedGroups.Add(group.ID);
                            group.IsBlocked = true;
                        }
                        break;
                    }
                default:
                    break;

            }
        }

        private void LeaveGroupCommandAction(IChatGroup group)
        {
            switch (group.Type)
            {
                case GroupType.JiveChatGroup:
                    Messenger.Default.Send(new NotificationMessageAction<bool>(this, new JiveDialogQuery("Leave group?",string.Format("Are you sure you wish to leave {0}?", group.Name)), "QueryDialog", async result =>
                        {
                            if ((bool)result == true)
                                await _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.LeaveGroup) { RecipientID = group.ID });
                        }));
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        private async void IgnoreContactCommandAction(IContact contact)
        {
            if (contact.IsIgnored)
            {
                await _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.UnignoreContact) { RecipientID = contact.UserName });
                _currentClient.UserSettings.IgnoredContacts.Remove(contact.UserName);
                contact.IsIgnored = false;
            }
            else
            {
                await _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.IgnoreContact) { RecipientID = contact.UserName });
                _currentClient.UserSettings.IgnoredContacts.Add(contact.UserName);
                contact.IsIgnored = true;
            }
        }

        private void OpenNotificationsCommandAction()
        {
            Messenger.Default.Send(new NotificationMessage(this, "OpenNotifications"));
        }

        private void OpenPmCommandAction(IContact contact)
        {
            if (contact == null)
                return;

            _currentClient.OpenNewPM(contact.UserID);
        }

        private bool CanExecuteAsGroupAdmin(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner || priv == GroupPrivilege.Moderator)
                                return true;
                        }
                        break;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteAsGroupOwner(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner)
                                return true;
                        }
                        break;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteLeaveGroup(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner)
                                return false;
                        }
                        return true;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteBlockGroup(object obj)
        {
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        return true;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }


        private void InviteContactToGroupCommandAction(IChatGroup group)
        {
            _navigationService.NavigateTo("InviteToGroupPage", group);
            //Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, group, "InviteContactToChatGroup"));
        }

        private void CreateGroupCommandAction()
        {
            Messenger.Default.Send(new NotificationMessage(this, "OpenCreateGroup"));
        }

        //private void AddContactCommandAction(IContact contact)
        //{
        //    object target = contact != null ? contact.UserName : null;
        //    Messenger.Default.Send(new NotificationMessage(this, target, "OpenAddContacts"));
        //}
        public void RunOnNetworkConnect()
        {
            if (Contacts == null)
                Contacts = new ObservableCollection<IContact>();

            this._isConnected = true;

            this.OpenAddContactPageCommand.RaiseCanExecuteChanged();
            this.OpenGroupAndContactPageCommand.RaiseCanExecuteChanged();
        }



        public override void Cleanup()
        {
            base.Cleanup();

            this.Contacts = new ObservableCollection<IContact>();

            this.CurrentUser = null;
            SimpleIoc.Default.Unregister<ViewModelContactList>(VmHelper.Locator.ViewModelContactListKey);
            VmHelper.Locator.ViewModelContactListKey = System.Guid.NewGuid().ToString();
        }


        public void RunOnNetworkDisconnect()
        {
            this.Cleanup();
            this.OpenAddContactPageCommand.RaiseCanExecuteChanged();
            this.OpenGroupAndContactPageCommand.RaiseCanExecuteChanged();
        }
        public void AddOrUpdateContact(UserInfo user, bool createVisualGroups)
        {
            if (Contacts == null)
            {
                Contacts = new ObservableCollection<IContact>();
            }
            bool groupAddedOrRemoved = false;

            bool isFriend = user.Equals(_currentClient.CurrentUserInfo) ? true : this._currentClient.FriendUsernames.Any(x => x == user.UserName);
            bool addContact = false;
            JiveContactModel contact = Contacts.FirstOrDefault(x => x.UserID == user.UserID) as JiveContactModel;
            if (contact == null)
            {
                if (string.IsNullOrEmpty(user.FontStyle))
                    user.FontStyle = "Normal";
                if (string.IsNullOrEmpty(user.FontFamily))
                    user.FontFamily = "Segoe UI";

                contact = new JiveContactModel(user.ID, user.UserName, user.PrettyName, SharedStaticMethods.GetDeviceType(user.UserID), isFriend)
                {
                    FontWeight = StaticMethods.ParseFontWeight(user.FontWeight),
                    FontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), user.FontStyle),
                    FontFamily = new FontFamily(user.FontFamily),
                    FontColor = StaticMethods.HexStringToColor(user.Color),
                    FontSize = _currentClient.UserSettings.FontSize.GetValueOrDefault(12)
                };
                contact.ShowInContactList = true;

                addContact = true;
            }
            else
            {
                if (user.FontFamily != null || contact.FontFamily == null)
                    contact.FontFamily = !string.IsNullOrEmpty(user.FontFamily) ? new FontFamily(user.FontFamily) : new FontFamily("Segoe UI");
                if (user.Color != null || contact.FontColor == null)
                    contact.FontColor = StaticMethods.HexStringToColor(user.Color);
                if (!string.IsNullOrEmpty(user.FontWeight))
                    contact.FontWeight = StaticMethods.ParseFontWeight(user.FontWeight);
                if (!string.IsNullOrEmpty(user.FontStyle))
                    contact.FontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), user.FontStyle);
                //also add font from user settings.
                if (_currentClient.UserSettings.FontSize.HasValue)
                    contact.FontSize = _currentClient.UserSettings.FontSize.Value;
            }

            if (user.ClientStatus.HasValue)
                contact.ClientStatus = user.ClientStatus.Value;

            if (user.NickName != null)
            {
                contact.NickName = user.NickName;
                Messenger.Default.Send(new NotificationMessage<IContact>(this, contact, "UpdatePrivateMessage"));
            }

            contact.IsFriend = isFriend;
            contact.IsIgnored = _currentClient.UserSettings.IgnoredContacts.Any(x => x == user.UserName);
            if (user.TimestampAfk.HasValue)
                contact.AfkStart = user.TimestampAfk.Value.DateTime;
            if (user.TimestampOffline.HasValue)
                contact.OfflineStart = user.TimestampOffline.Value.DateTime;


            var newChatGroups = new ObservableCollection<JiveChatGroupModel>(user.ChatGroups.Select(x => new JiveChatGroupModel(x.ID, x.Name, x.GroupMembers))) ?? new ObservableCollection<JiveChatGroupModel>();

            if (user.Equals(_currentClient.CurrentUserInfo))
            {

                //This is our own user, so we update our chat groups.
                CurrentUser = contact;

                foreach (JiveChatGroupModel group in newChatGroups)
                {
                    if (!contact.ChatGroups.Contains(group))
                    {
                        contact.ChatGroups.Add(group);
                        groupAddedOrRemoved = true;
                        createVisualGroups = true;
                    }

                }
                foreach (IChatGroup m in new List<IChatGroup>(contact.ChatGroups))
                {
                    switch (m.Type)
                    {
                        case GroupType.JiveChatGroup:
                            ((JiveChatGroupModel)m).IsBlocked = _currentClient.UserSettings.BlockedGroups.Any(x => x == m.ID);
                            if (!newChatGroups.Contains(m))
                            {
                                contact.ChatGroups.Remove(m);
                                groupAddedOrRemoved = true;
                                createVisualGroups = true;
                                Messenger.Default.Send(new NotificationMessage<string>(this, m.ID, "CloseTab"));
                            }
                            break;
                        default:
                            break;
                    }
                }

                if (!Array.Equals(contact.ChatGroups, newChatGroups))
                {
                    foreach (var grp in newChatGroups)
                    {
                        Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, grp, "UpdateGroup"));
                    }
                }

                if (user.CustomEmoticons != null)
                {
                    Task.Run(async () =>
                    {
                        await Task.Delay(500);
                        await DispatcherHelper.RunAsync(async () =>
                        {
                            var customEmoticons = CreateCustomEmoticonList(contact, user.CustomEmoticons);
                            contact.CustomEmoticons = new ObservableCollection<JiveCustomEmoticonModel>();

                            foreach (JiveCustomEmoticonModel model in customEmoticons)
                            {
                                contact.CustomEmoticons.Add(model);
                                await Task.Delay(20);
                            }
                            contact.RecentlyUsedEmoticons = CreateRecentlyUsedEmoticonList();
                        });
                    });
                }

            }
            else
            {
                if (user.CustomEmoticons != null)
                {
                    contact.CustomEmoticons = CreateCustomEmoticonList(contact, user.CustomEmoticons);
                }
                contact.ChatGroups = new ObservableCollection<IChatGroup>(newChatGroups);
            }


            if (user.Avatar != null && !String.IsNullOrEmpty(user.Avatar.Hash) && !String.IsNullOrEmpty(user.Avatar.Extension))
            {
                try
                {
                    contact.Avatar = string.Format("ms-appdata:///local/{0}/Avatars/{1}", contact.UserName, user.Avatar.Hash + user.Avatar.Extension);//await StaticMethods.LoadAvatarFromIsolatedStorage(contact.UserName, user.Avatar.Hash + user.Avatar.Extension);
                }
                catch (Exception ex)
                {
                    Logger.ErrorException("Error loading avatar", ex);
                }
            }
            else
                contact.Avatar = "ms-appx:///Assets/Images/avatar_empty_120x120.png";


            if (CurrentUser != null)
            {
                //Iterate through the chat groups that the contact owns and we are a member of, and update changes.
                foreach (JiveChatGroupModel group in newChatGroups.Where(group => this.CurrentUser.ChatGroups.Any(x => x.ID == group.ID)))
                {
                    JiveChatGroupModel grp = (JiveChatGroupModel)this.CurrentUser.ChatGroups.FirstOrDefault(x => x.Type == GroupType.JiveChatGroup && x.ID == group.ID);

                    grp.GroupMembers = group.GroupMembers;
                    if (group.GroupMembers.Any(x => x.Key == user.UserName && x.Value == GroupPrivilege.Owner))
                    {
                        grp.Name = group.Name;
                        Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, grp, "UpdateGroup"));
                    }

                }
            }

            if (addContact)
            {
                Contacts.Add(contact);
                Logger.Debug("Added contact: " + contact.UserID);
            }

            var onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName && x.DeviceType == DeviceType.PC && x.ClientStatus != ClientStatus.Offline);
            if (onlineContact == null)
                onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName && x.ClientStatus != ClientStatus.Offline);
            if (onlineContact == null)
                onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName);
            if (onlineContact != null)
            {
                onlineContact.ShowInContactList = true;
                var otherUsersClients = this.Contacts.Where(x => x.UserName == onlineContact.UserName && x.DeviceID != onlineContact.DeviceID);
                foreach (IContact cont in otherUsersClients)
                {
                    cont.ShowInContactList = false;
                }
            }
            if (createVisualGroups)//(addContact || groupAddedOrRemoved) && createVisualGroups)
            {
                GroupedContacts = CreateVisualGroups();
            }
            else
            {
                foreach (VisualGroup grp in GroupedContacts)
                {
                    SortContactsByNameAndStatus(grp.Contacts);
                }
            }
        }


        private void SortContactsByNameAndStatus(ObservableCollection<IContact> contacts)
        {
            List<IContact> sorted = contacts.OrderBy(x =>
                {
                    if (x.ClientStatus == ClientStatus.Offline)
                        return true;
                    else
                        return false;
                })
                .ThenBy(x => x.PrettyName).ToList();

            int index = 0;
            while (index < sorted.Count)
            {
                if (!contacts[index].Equals(sorted[index]))
                {
                    IContact t = contacts[index];
                    contacts.Move(index, sorted.IndexOf(t));
                    //contacts.RemoveAt(index);
                    // contacts.Insert(sorted.IndexOf(t), t);
                }
                else
                {
                    index++;
                }
            }
        }
        private ObservableCollection<VisualGroup> CreateVisualGroups()
        {
            var groupedContacts = new ObservableCollection<VisualGroup>();
            if (this.CurrentUser == null)
                return groupedContacts;
            VisualGroup friendsGroup = new VisualGroup(new JiveFriendsGroupModel("JiveFriendsGroup", "All Contacts"));
            var friendContacts = new ObservableCollection<IContact>(Contacts.Where(x => x.ShowInContactList));
            SortContactsByNameAndStatus(friendContacts);
            friendsGroup.Contacts = friendContacts;

            groupedContacts.Add(friendsGroup);
            foreach (IChatGroup myGroup in this.CurrentUser.ChatGroups)
            {
                VisualGroup vGrp = new VisualGroup(myGroup);
                var grpContacts = new ObservableCollection<IContact>(Contacts.Where(contact => contact.ShowInContactList && contact.ChatGroups.Any(contactsGrp => contactsGrp.ID == myGroup.ID)));
                SortContactsByNameAndStatus(grpContacts);
                vGrp.Contacts = grpContacts;
                groupedContacts.Add(vGrp);
            }
            return groupedContacts;
        }

        private ObservableCollection<JiveCustomEmoticonModel> CreateCustomEmoticonList(IContact user, IEnumerable<ContactEmoticon> customEmoticons)
        {
            ObservableCollection<JiveCustomEmoticonModel> list = new ObservableCollection<JiveCustomEmoticonModel>();
            foreach (ContactEmoticon ce in customEmoticons.OrderBy(x => x.Index))
            {
                // string folder = StaticSettings.CustomEmoticonFolder;//user.Equals(this.CurrentUser) ? StaticSettings.CustomEmoticonFolder : StaticSettings.CustomContactEmoticonFolder;
                string filePath = StaticMethods.GetCustomEmoticonPath(user.UserName, ce.Hash + ce.Extension);//StaticMethods.CombinePath(folder, user.UserName, ce.Hash + ce.Extension);
                list.Add(new JiveCustomEmoticonModel() { EmoticonShortcut = ce.Keyword, EmoticonPath = filePath });
            }
            return list;
        }

        private ObservableCollection<IEmoticon> CreateRecentlyUsedEmoticonList()
        {
            Dictionary<string, int> customEmoticonDic = AppSettings.Instance.RecentlyUsedCustomEmoticons;
            Dictionary<string, int> defaultEmoticonDic = AppSettings.Instance.RecentlyUsedDefaultEmoticons;
            foreach (var v in customEmoticonDic)
            {
                var model = CurrentUser.CustomEmoticons.FirstOrDefault(x => (string)x.EmoticonShortcut == v.Key);
                if (!CurrentUser.RecentlyUsedEmoticons.Contains(model))
                    CurrentUser.RecentlyUsedEmoticons.Add(model);
            }
            foreach (var v in defaultEmoticonDic)
            {
                var model = _currentClient.DefaultEmoticons.FirstOrDefault(x => ((string[])x.EmoticonShortcut).Contains(v.Key));
                if (!CurrentUser.RecentlyUsedEmoticons.Contains(model))
                    CurrentUser.RecentlyUsedEmoticons.Add(model);
            }
            var sortedList = _currentUser.RecentlyUsedEmoticons.ToList();

            sortedList.Sort((x, y) =>
            {
                int thisCount = 0;

                if (x.Type == EmoticonType.CustomEmoticon)
                    thisCount = customEmoticonDic[(string)((JiveCustomEmoticonModel)x).EmoticonShortcut];
                else if (x.Type == EmoticonType.DefaultEmoticon)
                {
                    var shortcut = (string[])((JiveDefaultEmoticonModel)x).EmoticonShortcut;
                    thisCount = defaultEmoticonDic.Where(emoticon => shortcut.Contains(emoticon.Key)).First().Value;
                }

                int otherCount = 0;

                if (y.Type == EmoticonType.CustomEmoticon)
                    otherCount = customEmoticonDic[(string)((JiveCustomEmoticonModel)y).EmoticonShortcut];
                else if (y.Type == EmoticonType.DefaultEmoticon)
                {
                    var shortcut = (string[])((JiveDefaultEmoticonModel)y).EmoticonShortcut;
                    otherCount = defaultEmoticonDic.Where(emoticon => shortcut.Contains(emoticon.Key)).First().Value;
                }

                if (thisCount == otherCount)
                    return 0;
                else if (thisCount > otherCount)
                    return -1;
                else
                    return 1;

            });
            return new ObservableCollection<IEmoticon>(sortedList);
        }

        public void SetContactAsOffline(string userID)
        {
            IContact model = Contacts.FirstOrDefault(x => x.UserID == userID);
            if (model != null)
            {
                //if (!_currentClient.ConnectedClients.Values.Any(x => x.UserName == userID))
                //{
                    model.ClientStatus = ClientStatus.Offline;
                    if (model is JiveContactModel)
                    {
                        ((JiveContactModel)model).OfflineStart = DateTime.Now;
                    }
                    Logger.Debug("Marked contact as offline: " + model.UserName);
                //}
            }
        }
    }
}